﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using Postnuclear.Dependency;

namespace Postnuclear.Collections
  {
  /// <content>
  /// List node.
  /// </content>
  public sealed partial class ObservableLinkedList<T>
    {
    /// <summary>
    /// Represents a node in a <see cref="ObservableLinkedList{T}"/>. 
    /// Provides notifications when <see cref="Next"/> or <see cref="Previous"/> changes.
    /// </summary>
    public sealed class Node
      : IValueProvider<T>, INotifyPropertyChanged
      {
      #region Public

      // Constructors

      /// <summary>
      /// Initializes a new instance of the <see cref="Node"/> class.
      /// </summary>
      /// <param name="value">The value to contain in the <see cref="Node"/>.</param>
      public Node(T value)
        : this()
        {
        Contract.Ensures(object.Equals(Value, value));

        Value = value;
        }

      /// <summary>
      /// Initializes a new instance of the <see cref="Node"/> class.
      /// </summary>
      public Node()
        {
        Expressions.Bind(() => Index, () => List != null ? (Previous != null ? Previous.Index + 1 : 0) : -1);
        }

      // Events

      /// <summary>
      /// Occurs when the <see cref="Index"/> changes.
      /// </summary>
      public event EventHandler<EventArgs> IndexChanged;

      /// <summary>
      /// Occurs when the <see cref="List"/> changes.
      /// </summary>
      public event EventHandler<EventArgs> ListChanged;

      /// <summary>
      /// Occurs when the <see cref="Next"/> changes.
      /// </summary>
      public event EventHandler<EventArgs> NextChanged;

      /// <summary>
      /// Occurs when the <see cref="Previous"/> changes.
      /// </summary>
      public event EventHandler<EventArgs> PreviousChanged;

      /// <summary>
      /// Occurs when the <see cref="Value"/> changes.
      /// </summary>
      public event EventHandler<EventArgs> ValueChanged;

      /// <summary>
      /// Occurs when the <see cref="Value"/> changes.
      /// </summary>
      internal event EventHandler<ValueEventArgs<KeyValuePair<T, T>>> InternalValueChanged;

      /// <summary>
      /// Occurs when a property value changes.
      /// </summary>
      public event PropertyChangedEventHandler PropertyChanged;

      // Properties

      /// <summary>
      /// Gets the index of the node in <see cref="List"/>.
      /// </summary>
      public int Index
        {
        [Pure]
        get
          {
          return _index;
          }

        private set
          {
          if (value == _index)
            {
            return;
            }

          _index = value;
          IndexChanged.Raise(this);
          PropertyChanged.Raise(this, "Index");
          }
        }

      /// <summary>
      /// Gets the <see cref="ObservableLinkedList{T}"/> that the <see cref="Node"/> belongs to.
      /// </summary>
      public ObservableLinkedList<T> List
        {
        [Pure]
        get
          {
          return _list;
          }

        internal set
          {
          if (value == _list)
            {
            return;
            }

          _list = value;
          ListChanged.Raise(this);
          PropertyChanged.Raise(this, "List");
          }
        }

      /// <summary>
      /// Gets the next node in the <see cref="ObservableLinkedList{T}"/>.
      /// </summary>
      public Node Next
        {
        [Pure]
        get
          {
          return _next;
          }

        internal set
          {
          if (value == _next)
            {
            return;
            }

          _next = value;
          NextChanged.Raise(this);
          PropertyChanged.Raise(this, "Next");
          }
        }

      /// <summary>
      /// Gets the previous node in the <see cref="ObservableLinkedList{T}"/>.
      /// </summary>
      public Node Previous
        {
        [Pure]
        get
          {
          return _previous;
          }

        internal set
          {
          if (value == _previous)
            {
            return;
            }

          _previous = value;
          PreviousChanged.Raise(this);
          PropertyChanged.Raise(this, "Previous");
          }
        }

      /// <summary>
      /// Gets or sets the value contained in the node.
      /// </summary>
      public T Value
        {
        [Pure]
        get
          {
          return _value;
          }

        set
          {
          if (object.Equals(value, _value))
            {
            return;
            }

          T oldValue = _value;
          _value = value;
          ValueChanged.Raise(this);
          PropertyChanged.Raise(this, "Value");
          InternalValueChanged.Raise(this, new KeyValuePair<T, T>(oldValue, _value));
          }
        }

      #endregion

      #region Private

      // Methods

      [ContractInvariantMethod]
      private void Validate()
        {
        Contract.Invariant(List != null || Index == -1);
        Contract.Invariant(List == null || Previous != null || Index == 0);
        Contract.Invariant(List == null || Previous == null || Index == Previous.Index + 1);
        }

      // Fields

      [DebuggerBrowsable(DebuggerBrowsableState.Never)]
      private ObservableLinkedList<T> _list;

      [DebuggerBrowsable(DebuggerBrowsableState.Never)]
      private Node _next;

      [DebuggerBrowsable(DebuggerBrowsableState.Never)]
      private Node _previous;

      [DebuggerBrowsable(DebuggerBrowsableState.Never)]
      private T _value;

      [DebuggerBrowsable(DebuggerBrowsableState.Never)]
      private int _index;

      #endregion
      }
    }
  }